﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Client;
using System.Threading;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Data;
using Budgetsch.Web.EntityModel;
using Budgetsch.Web.Services;
using GalaSoft.MvvmLight;
using System.Linq;

namespace Budgetsch.ViewModels.Statistics
{
    public class StatisticsVM : ViewModelBase
    {
        #region Private members

        private readonly BudgetschDomainContext m_context;
        private ObservableCollection<PieElement> m_pieElements = null;
        private string m_currentDomain;
        private readonly IEnumerable<string> m_domains = new[] { "Incomes", "Costs", "Savings" };
        private bool m_isInMonthMode = true;
        private bool m_isBusy = true;
        private bool m_isPieBusy = true;
        private bool m_isStackedBusy = true;
        private bool m_isStackReady = false;

        #endregion

        #region Public Properties

        public const string PieElementsPropertyName = "PieElements";
        public const string IsInMonthModePropertyName = "IsInMonthMode";
        public const string CurrentDomainPropertyName = "CurrentDomain";
        public const string IsBusyPropertyName = "IsBusy";
        public const string IsStackReadyPropertyName = "IsStackReady";

        public IEnumerable<string> Domains { get { return m_domains; } }

        public string CurrentDomain
        {
            get
            {
                return m_currentDomain;
            }
            set
            {
                m_currentDomain = value;
                ResetBusyIndicators();
                GeneratePieData();
                RaisePropertyChanged(() => CurrentDomain);
            }
        }

        public bool IsInMonthMode
        {
            get
            {
                return m_isInMonthMode;
            }
            set
            {
                m_isInMonthMode = value;
                ResetBusyIndicators();
                GeneratePieData();
                RaisePropertyChanged(() => IsInMonthMode);
            }
        }

        public ObservableCollection<PieElement> PieElements
        {
            get
            {
                return m_pieElements;
            }

            set
            {
                if (m_pieElements == value)
                {
                    return;
                }

                m_pieElements = value;
                RaisePropertyChanged(() => PieElements);
            }
        }

        public bool IsBusy
        {
            get
            {
                return m_isPieBusy || m_isStackedBusy;
            }

            set
            {
                if (m_isBusy == value)
                {
                    return;
                }

                m_isBusy = value;
                RaisePropertyChanged(() => IsBusy);
            }
        }

        public bool IsStackReady
        {
            get
            {
                return m_isStackReady;
            }

            set
            {
                if (m_isStackReady == value)
                {
                    return;
                }

                m_isStackReady = value;
                RaisePropertyChanged(() => IsStackReady);
            }
        }

        #endregion

        public StatisticsVM()
        {
            m_context = new BudgetschDomainContext();
            PieElements = new ObservableCollection<PieElement>();

            m_context.Load(m_context.GetIncomesQuery(), x =>
                                                            {
                                                                IsStackReady = true;
                                                            }, 0);
            m_context.Load(m_context.GetIncomeCategoriesWithItemsQuery(), x =>
            {
                CurrentDomain = Domains.First(); //this does pie gen. as well
                m_isPieBusy = false; RaisePropertyChanged(() => IsBusy);
            }, 0);

        }

        #region Stacked visualization methods

        public Collection<SeriesDefinition> GetStackedChartInfo()
        {
            var items = GenerateStackedData();
            m_isStackedBusy = false; RaisePropertyChanged(() => IsBusy);
            return items;
        }

        private Collection<SeriesDefinition> GenerateStackedData()
        {
            switch (CurrentDomain)
            {
                case "Incomes":
                    return GenerateStackedData_Incomes();
                case "Costs":
                    return GenerateStackedData_Costs();
                case "Savings":
                    return GenerateStackedData_Savings();
                default:
                    return new Collection<SeriesDefinition>();
            }
        }

        private IEnumerable<DateTime> GeneratedOccurences(DateTime start_, int delta_, DateTime end_)
        {
            var date = start_;
            do
            {
                yield return date;
                date = date + new TimeSpan(7 * delta_, 0, 0, 0);
            } while (date <= end_);
        }

        private Collection<SeriesDefinition> GenerateStackedData_Incomes()
        {
            var definitions = new Collection<SeriesDefinition>();
            var incomes = new List<Income>();
            //create list of incomes
            foreach (var item in m_context.Incomes)
            {
                incomes.Add(item);
            }
            //grab all category names that exist amongst incomes
            var categories = incomes.Select(i => i.IncomeCategory.Name).Distinct();

            //generate reoccurent items and use them from now on
            var recurrents = incomes.Where(i => i.IsRecurring).ToList();
            foreach (var recurrent in recurrents)
            {
                var dates = GeneratedOccurences(recurrent.StartDate, recurrent.Delta.Value, recurrent.EndDate.Value);
                foreach (var date in dates)
                {
                    if (date != recurrent.StartDate)
                        incomes.Add(new Income()
                        {
                            Amount = recurrent.Amount,
                            IncomeCategory = recurrent.IncomeCategory,
                            StartDate = date
                        });
                }
            }

            //create time-series for each categories
            if (IsInMonthMode)
            {
                var min = incomes.Select(i => i.StartDate).Min();

                foreach (var category in categories)
                {
                    var date = new DateTime(min.Year, min.Month, 1);
                    var elements = new List<StackElement>();
                    while (date <= DateTime.Now)
                    {
                        var item = new StackElement();
                        item.Date = string.Format("{0}/{1}", date.Year, date.Month);

                        var date2 = date.AddMonths(1);
                        item.Amount = incomes.Where(i => i.StartDate >= date && i.StartDate < date2).Where(
                            i => i.IncomeCategory.Name == category).Sum(i => i.Amount);
                        elements.Add(item);
                        date = date2;
                    }
                    definitions.Add(new SeriesDefinition() { DependentValuePath = "Amount", IndependentValuePath = "Date", Title = category, ItemsSource = elements });
                }
            }
            else //if is in year mode
            {
                var min = incomes.Select(i => i.StartDate).Min();
                var date = new DateTime(min.Year, 1, 1);

                foreach (var category in categories)
                {
                    var elements = new List<StackElement>();
                    while (date <= DateTime.Now)
                    {
                        var item = new StackElement();
                        item.Date = date.Year.ToString();

                        var date2 = date.AddYears(1);
                        item.Amount = incomes.Where(i => i.StartDate >= date && i.StartDate < date2).Where(
                            i => i.IncomeCategory.Name == category).Sum(i => i.Amount);
                        date = date2;
                    }
                    definitions.Add(new SeriesDefinition() { DependentValuePath = "Amount", IndependentValuePath = "Date", Title = category, ItemsSource = elements });
                }
            }

            return definitions;
        }


        private Collection<SeriesDefinition> GenerateStackedData_Costs()
        {
            throw new NotImplementedException();
        }

        private Collection<SeriesDefinition> GenerateStackedData_Savings()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Pie visualization methods

        private void GeneratePieData()
        {
            switch (CurrentDomain)
            {
                case "Incomes":
                    GeneratePieData_Income();
                    break;
                case "Costs":
                    GeneratePieData_Costs();
                    break;
                case "Savings":
                    GeneratePieData_Savings();
                    break;
                default:
                    break;
            }

            m_isPieBusy = false; RaisePropertyChanged(() => IsBusy);
        }

        private void GeneratePieData_Income()
        {
            if (PieElements.Count > 0)
            {
                PieElements.Clear();
            }
            else
            {
                foreach (var ic in m_context.IncomeCategories)
                {
                    PieElements.Add(new PieElement()
                    {
                        Category = ic.Name,
                        Count = ic.Incomes.Count
                    });
                }
            }
        }

        private void GeneratePieData_Costs()
        {
            var pieElements = new List<PieElement>();
            foreach (var cc in m_context.CostCategories)
            {
                pieElements.Add(new PieElement()
                                    {
                                        Category = cc.Name,
                                        Count = cc.Costs.Count
                                    });
            }
        }

        private void GeneratePieData_Savings()
        {
            var pieElements = new List<PieElement>();
            foreach (var sc in m_context.SavingCategories)
            {
                pieElements.Add(new PieElement()
                                    {
                                        Category = sc.Name,
                                        Count = sc.Savings.Count
                                    });
            }
        }

        #endregion

        private void ResetBusyIndicators()
        {
            m_isPieBusy = true;
            m_isStackedBusy = true;
        }
    }

    public class PieElement
    {
        public string Category { get; set; }
        public int Count { get; set; }
    }

    public class StackElement
    {
        public decimal Amount { get; set; }
        public string Date { get; set; }
    }
}